home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / module_db.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-05-22  |  27.1 KB  |  1,149 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * module_db.c (C) 1999 Austin Donnelly <austin@gimp.org>
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  */
  20.  
  21. #include "config.h"
  22.  
  23. #include <glib.h>
  24.  
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #ifdef HAVE_UNISTD_H
  30. #include <unistd.h>
  31. #endif
  32. #include <time.h>
  33.  
  34. #include <gtk/gtk.h>
  35.  
  36. #include "apptypes.h"
  37.  
  38. #include "appenv.h"
  39. #include "module_db.h"
  40. #include "gimpsignal.h"
  41. #include "gimprc.h"
  42. #include "datafiles.h"
  43. #include "gimpset.h"
  44. #include "gimpui.h"
  45.  
  46. #include "libgimp/gimpenv.h"
  47. #include "libgimp/gimpmodule.h"
  48.  
  49. #include "libgimp/gimpintl.h"
  50.  
  51.  
  52. /* export this to gimprc.c */
  53. gchar *module_db_load_inhibit = NULL;
  54.  
  55. typedef enum
  56. {
  57.   ST_MODULE_ERROR,         /* missing module_load function or other error    */
  58.   ST_LOADED_OK,            /* happy and running (normal state of affairs)    */
  59.   ST_LOAD_FAILED,          /* module_load returned GIMP_MODULE_UNLOAD        */
  60.   ST_UNLOAD_REQUESTED,     /* sent unload request, waiting for callback      */
  61.   ST_UNLOADED_OK           /* callback arrived, module not in memory anymore */
  62. } module_state;
  63.  
  64. static const gchar * const statename[] =
  65. {
  66.   N_("Module error"),
  67.   N_("Loaded OK"),
  68.   N_("Load failed"),
  69.   N_("Unload requested"),
  70.   N_("Unloaded OK")
  71. };
  72.  
  73. #ifdef __EMX__
  74. extern void gimp_color_selector_register   ();
  75. extern void gimp_color_selector_unregister ();
  76. extern void dialog_register                ();
  77. extern void dialog_unregister              ();
  78.  
  79. static struct main_funcs_struc
  80. {
  81.   gchar *name;
  82.   void (*func) ();
  83.  
  84. gimp_main_funcs[] =
  85. {
  86.   { "gimp_color_selector_register",   gimp_color_selector_register },
  87.   { "gimp_color_selector_unregister", gimp_color_selector_unregister },
  88.   { "dialog_register",                dialog_register },
  89.   { "dialog_unregister",              dialog_unregister },
  90.   { NULL, NULL }
  91. };
  92. #endif
  93.  
  94.  
  95. /* one of these objects is kept per-module */
  96. typedef struct
  97. {
  98.   GtkObject       object;
  99.  
  100.   gchar          *fullpath;     /* path to the module                        */
  101.   module_state    state;        /* what's happened to the module             */
  102.   gboolean        ondisk;       /* TRUE if file still exists                 */
  103.   gboolean        load_inhibit; /* user requests not to load at boot time    */
  104.   gint            refs;         /* how many time we're running in the module */
  105.  
  106.   /* stuff from now on may be NULL depending on the state the module is in   */
  107.   GimpModuleInfo *info;         /* returned values from module_init          */
  108.   GModule        *module;       /* handle on the module                      */
  109.   gchar          *last_module_error;
  110.  
  111.   GimpModuleInitFunc   init;
  112.   GimpModuleUnloadFunc unload;
  113. } ModuleInfo;
  114.  
  115.  
  116. static guint module_info_get_type (void);
  117.  
  118. #define MODULE_INFO_TYPE    module_info_get_type()
  119. #define MODULE_INFO(obj)    GTK_CHECK_CAST (obj, MODULE_INFO_TYPE, ModuleInfo)
  120. #define IS_MODULE_INFO(obj) GTK_CHECK_TYPE (obj, MODULE_INFO_TYPE)
  121.  
  122.  
  123. #define NUM_INFO_LINES 7
  124.  
  125. typedef struct
  126. {
  127.   GtkWidget  *table;
  128.   GtkWidget  *label[NUM_INFO_LINES];
  129.   GtkWidget  *button_label;
  130.   ModuleInfo *last_update;
  131.   GtkWidget  *button;
  132.   GtkWidget  *list;
  133.   GtkWidget  *load_inhibit_check;
  134. } BrowserState;
  135.  
  136. /* global set of module_info pointers */
  137. static GimpSet          *modules;
  138. static GimpSetHandlerId  modules_handler;
  139.  
  140. /* If the inhibit state of any modules changes, we might need to
  141.  * re-write the modulerc. */
  142. static gboolean need_to_rewrite_modulerc = FALSE;
  143.  
  144.  
  145. /* debug control: */
  146.  
  147. /*#define DUMP_DB*/
  148. /*#define DEBUG*/
  149.  
  150. #ifdef DEBUG
  151. #undef DUMP_DB
  152. #define DUMP_DB
  153. #define TRC(x) printf x
  154. #else
  155. #define TRC(x)
  156. #endif
  157.  
  158.  
  159. /* prototypes */
  160. static void         module_initialize      (gchar        *filename);
  161. static void         mod_load               (ModuleInfo   *mod,
  162.                         gboolean      verbose);
  163. static void         mod_unload             (ModuleInfo   *mod,
  164.                         gboolean      verbose);
  165. static gboolean     mod_idle_unref         (ModuleInfo   *mod);
  166. static ModuleInfo * module_find_by_path    (const gchar  *fullpath);
  167.  
  168. #ifdef DUMP_DB
  169. static void   print_module_info            (gpointer      data,
  170.                         gpointer      user_data);
  171. #endif
  172.  
  173. static void   browser_popdown_callback     (GtkWidget    *widget,
  174.                         gpointer      data);
  175. static void   browser_destroy_callback     (GtkWidget    *widget,
  176.                         gpointer      data);
  177. static void   browser_info_update          (ModuleInfo   *mod,
  178.                         BrowserState *st);
  179. static void   browser_info_add             (GimpSet      *set,
  180.                         ModuleInfo   *mod,
  181.                         BrowserState *st);
  182. static void   browser_info_remove          (GimpSet      *set,
  183.                         ModuleInfo   *mod,
  184.                         BrowserState *st);
  185. static void   browser_info_init            (BrowserState *st,
  186.                         GtkWidget    *table);
  187. static void   browser_select_callback      (GtkWidget    *widget,
  188.                         GtkWidget    *child);
  189. static void   browser_load_unload_callback (GtkWidget    *widget,
  190.                         gpointer      data);
  191. static void   browser_refresh_callback     (GtkWidget    *widget,
  192.                         gpointer      data);
  193. static void   make_list_item               (gpointer      data,
  194.                         gpointer      user_data);
  195.  
  196. static void   gimp_module_ref              (ModuleInfo   *mod);
  197. static void   gimp_module_unref            (ModuleInfo   *mod);
  198.  
  199.  
  200.  
  201. /**************************************************************/
  202. /* Exported functions */
  203.  
  204. void
  205. module_db_init (void)
  206. {
  207.   gchar *filename;
  208.  
  209.   /* load the modulerc file */
  210.   filename = gimp_personal_rc_file ("modulerc");
  211.   parse_gimprc_file (filename);
  212.   g_free (filename);
  213.  
  214.   /* Load and initialize gimp modules */
  215.  
  216.   modules = gimp_set_new (MODULE_INFO_TYPE, FALSE);
  217.  
  218.   if (g_module_supported ())
  219.     datafiles_read_directories (module_path,
  220.                 module_initialize, 0 /* no flags */);
  221. #ifdef DUMP_DB
  222.   gimp_set_foreach (modules, print_module_info, NULL);
  223. #endif
  224. }
  225.  
  226. static void
  227. free_a_single_module (gpointer data, 
  228.               gpointer user_data)
  229. {
  230.   ModuleInfo *mod = data;
  231.  
  232.   if (mod->module && mod->unload && mod->state == ST_LOADED_OK)
  233.     {
  234.       mod_unload (mod, FALSE);
  235.     }
  236. }
  237.  
  238. static void
  239. add_to_inhibit_string (gpointer data, 
  240.                gpointer user_data)
  241. {
  242.   ModuleInfo *mod = data;
  243.   GString    *str = user_data;
  244.  
  245.   if (mod->load_inhibit)
  246.     {
  247.       str = g_string_append_c (str, G_SEARCHPATH_SEPARATOR);
  248.       str = g_string_append (str, mod->fullpath);
  249.     }
  250. }
  251.  
  252.  
  253. static gboolean
  254. module_db_write_modulerc (void)
  255. {
  256.   GString  *str;
  257.   gchar    *p;
  258.   gchar    *filename;
  259.   FILE     *fp;
  260.   gboolean  saved = FALSE;
  261.  
  262.   str = g_string_new (NULL);
  263.   gimp_set_foreach (modules, add_to_inhibit_string, str);
  264.   if (str->len > 0)
  265.     p = str->str + 1;
  266.   else
  267.     p = "";
  268.  
  269.   filename = gimp_personal_rc_file ("modulerc");
  270.   fp = fopen (filename, "wt");
  271.   g_free (filename);
  272.   if (fp)
  273.     {
  274.       fprintf (fp, "(module-load-inhibit \"%s\")\n", p);
  275.       fclose (fp);
  276.       saved = TRUE;
  277.     }
  278.  
  279.   g_string_free (str, TRUE);
  280.   return (saved);
  281. }
  282.  
  283.  
  284. void
  285. module_db_free (void)
  286. {
  287.   if (need_to_rewrite_modulerc)
  288.     {
  289.       if (module_db_write_modulerc ())
  290.     {
  291.       need_to_rewrite_modulerc = FALSE;
  292.     }
  293.     }
  294.   gimp_set_foreach (modules, free_a_single_module, NULL);
  295. }
  296.  
  297.  
  298. GtkWidget *
  299. module_db_browser_new (void)
  300. {
  301.   GtkWidget *shell;
  302.   GtkWidget *hbox;
  303.   GtkWidget *vbox;
  304.   GtkWidget *listbox;
  305.   GtkWidget *button;
  306.   BrowserState *st;
  307.  
  308.   shell = gimp_dialog_new (_("Module DB"), "module_db_dialog",
  309.                gimp_standard_help_func,
  310.                "dialogs/module_browser.html",
  311.                GTK_WIN_POS_NONE,
  312.                FALSE, TRUE, FALSE,
  313.  
  314.                _("OK"), browser_popdown_callback,
  315.                NULL, NULL, NULL, TRUE, TRUE,
  316.  
  317.                NULL);
  318.  
  319.   vbox = gtk_vbox_new (FALSE, 5);
  320.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  321.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (shell)->vbox), vbox);
  322.   gtk_widget_show (vbox);
  323.  
  324.   listbox = gtk_scrolled_window_new (NULL, NULL);
  325.   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
  326.                   GTK_POLICY_AUTOMATIC,
  327.                   GTK_POLICY_AUTOMATIC);
  328.   gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 0);
  329.   gtk_widget_set_usize (listbox, 125, 100);
  330.   gtk_widget_show (listbox);
  331.  
  332.   st = g_new0 (BrowserState, 1);
  333.  
  334.   st->list = gtk_list_new ();
  335.   gtk_list_set_selection_mode (GTK_LIST (st->list), GTK_SELECTION_BROWSE);
  336.   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (listbox),
  337.                      st->list);
  338.  
  339.   gimp_set_foreach (modules, make_list_item, st);
  340.  
  341.   gtk_widget_show (st->list);
  342.  
  343.   st->table = gtk_table_new (5, NUM_INFO_LINES + 1, FALSE);
  344.   gtk_table_set_col_spacings (GTK_TABLE (st->table), 4);  
  345.   gtk_box_pack_start (GTK_BOX (vbox), st->table, FALSE, FALSE, 0);
  346.   gtk_widget_show (st->table);
  347.  
  348.   hbox = gtk_hbutton_box_new ();
  349.   gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
  350.  
  351.   gtk_widget_show (hbox);
  352.   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 5);
  353.  
  354.   button = gtk_button_new_with_label (_("Refresh"));
  355.   gtk_widget_show (button);
  356.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  357.               browser_refresh_callback, st);
  358.   gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  359.  
  360.   st->button = gtk_button_new_with_label ("");
  361.   st->button_label = GTK_BIN (st->button)->child;
  362.   gtk_box_pack_start (GTK_BOX (hbox), st->button, TRUE, TRUE, 0);
  363.   gtk_widget_show (st->button);
  364.   gtk_signal_connect (GTK_OBJECT (st->button), "clicked",
  365.               browser_load_unload_callback, st);
  366.  
  367.   browser_info_init (st, st->table);
  368.   browser_info_update (st->last_update, st);
  369.  
  370.   gtk_object_set_user_data (GTK_OBJECT (st->list), st);
  371.  
  372.   gtk_signal_connect (GTK_OBJECT (st->list), "select_child",
  373.               browser_select_callback, NULL);
  374.  
  375.   /* hook the gimpset signals so we can refresh the display
  376.    * appropriately. */
  377.   modules_handler =
  378.     gimp_set_add_handler (modules, "modified", browser_info_update, st);
  379.  
  380.   gtk_signal_connect (GTK_OBJECT (modules), "add", 
  381.               browser_info_add, st);
  382.   gtk_signal_connect (GTK_OBJECT (modules), "remove", 
  383.               browser_info_remove, st);
  384.  
  385.   gtk_signal_connect (GTK_OBJECT (shell), "destroy",
  386.               browser_destroy_callback, st);
  387.  
  388.   return shell;
  389. }
  390.  
  391.  
  392. /**************************************************************/
  393. /* ModuleInfo object glue */
  394.  
  395.  
  396. typedef struct
  397. {
  398.   GtkObjectClass parent_class;
  399. } ModuleInfoClass;
  400.  
  401. enum
  402. {
  403.   MODIFIED,
  404.   LAST_SIGNAL
  405. };
  406. static guint module_info_signals[LAST_SIGNAL];
  407.  
  408.  
  409. static void
  410. module_info_destroy (GtkObject *object)
  411. {
  412.   ModuleInfo *mod = MODULE_INFO (object);
  413.  
  414.   /* if this trips, then we're onto some serious lossage in a moment */
  415.   g_return_if_fail (mod->refs == 0);
  416.  
  417.   if (mod->last_module_error)
  418.     g_free (mod->last_module_error);
  419.   g_free (mod->fullpath);
  420. }
  421.  
  422. static void
  423. module_info_class_init (ModuleInfoClass *klass)
  424. {
  425.   GtkObjectClass *object_class;
  426.   GtkType type;
  427.  
  428.   object_class = GTK_OBJECT_CLASS (klass);
  429.  
  430.   type = object_class->type;
  431.  
  432.   object_class->destroy = module_info_destroy;
  433.  
  434.   module_info_signals[MODIFIED] =
  435.     gimp_signal_new ("modified", 0, type, 0, gimp_sigtype_void);
  436.  
  437.   gtk_object_class_add_signals (object_class, module_info_signals, LAST_SIGNAL);
  438. }
  439.  
  440. static void
  441. module_info_init (ModuleInfo *mod)
  442. {
  443.   /* don't need to do anything */
  444. }
  445.  
  446. static guint
  447. module_info_get_type (void)
  448. {
  449.   static guint module_info_type = 0;
  450.  
  451.   if (!module_info_type)
  452.     {
  453.       static const GtkTypeInfo module_info_info =
  454.       {
  455.     "ModuleInfo",
  456.     sizeof (ModuleInfo),
  457.     sizeof (ModuleInfoClass),
  458.     (GtkClassInitFunc) module_info_class_init,
  459.     (GtkObjectInitFunc) module_info_init,
  460.     /* reserved_1 */ NULL,
  461.         /* reserved_2 */ NULL,
  462.         (GtkClassInitFunc) NULL,
  463.       };
  464.  
  465.       module_info_type =
  466.     gtk_type_unique (gtk_object_get_type (), &module_info_info);
  467.     }
  468.  
  469.   return module_info_type;
  470. }
  471.  
  472. /* exported API: */
  473.  
  474. static void
  475. module_info_modified (ModuleInfo *mod)
  476. {
  477.   gtk_signal_emit (GTK_OBJECT (mod), module_info_signals[MODIFIED]);
  478. }
  479.  
  480. static ModuleInfo *
  481. module_info_new (void)
  482. {
  483.   return MODULE_INFO (gtk_type_new (module_info_get_type ()));
  484. }
  485.  
  486. static void
  487. module_info_free (ModuleInfo *mod)
  488. {
  489.   gtk_object_unref (GTK_OBJECT (mod));
  490. }
  491.  
  492.  
  493. /**************************************************************/
  494. /* helper functions */
  495.  
  496.  
  497. /* name must be of the form lib*.so (Unix) or *.dll (Win32) */
  498. static gboolean
  499. valid_module_name (const gchar *filename)
  500. {
  501.   const gchar *basename;
  502.   gint len;
  503.  
  504.   basename = g_basename (filename);
  505.  
  506.   len = strlen (basename);
  507.  
  508. #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN) && !defined(__EMX__)
  509.   if (len < 3 + 1 + 3)
  510.     return FALSE;
  511.  
  512.   if (strncmp (basename, "lib", 3))
  513.     return FALSE;
  514.  
  515.   if (strcmp (basename + len - 3, ".so"))
  516.     return FALSE;
  517. #else
  518.   if (len < 1 + 4)
  519.       return FALSE;
  520.  
  521.   if (g_strcasecmp (basename + len - 4, ".dll"))
  522.     return FALSE;
  523. #endif
  524.  
  525.   return TRUE;
  526. }
  527.  
  528.  
  529. static gboolean
  530. module_inhibited (const gchar *fullpath, 
  531.           const gchar *inhibit_list)
  532. {
  533.   gchar *p;
  534.   gint   pathlen;
  535.   const gchar *start;
  536.   const gchar *end;
  537.  
  538.   /* common case optimisation: the list is empty */
  539.   if (!inhibit_list || *inhibit_list == '\000')
  540.     return FALSE;
  541.  
  542.   p = strstr (inhibit_list, fullpath);
  543.   if (!p)
  544.     return FALSE;
  545.  
  546.   /* we have a substring, but check for colons either side */
  547.   start = p;
  548.   while (start != inhibit_list && *start != G_SEARCHPATH_SEPARATOR)
  549.       start--;
  550.   if (*start == G_SEARCHPATH_SEPARATOR)
  551.       start++;
  552.  
  553.   end = strchr (p, G_SEARCHPATH_SEPARATOR);
  554.   if (!end)
  555.     end = inhibit_list + strlen (inhibit_list);
  556.  
  557.   pathlen = strlen (fullpath);
  558.   if ((end - start) == pathlen)
  559.     return TRUE;
  560.   else
  561.     return FALSE;
  562. }
  563.  
  564.  
  565. static void
  566. module_initialize (gchar *filename)
  567. {
  568.   ModuleInfo *mod;
  569.  
  570.   if (!valid_module_name (filename))
  571.     return;
  572.  
  573.   /* don't load if we already know about it */
  574.   if (module_find_by_path (filename))
  575.     return;
  576.  
  577.   mod = module_info_new ();
  578.  
  579.   mod->fullpath = g_strdup (filename);
  580.   mod->ondisk = TRUE;
  581.   mod->state = ST_MODULE_ERROR;
  582.  
  583.   mod->info = NULL;
  584.   mod->module = NULL;
  585.   mod->last_module_error = NULL;
  586.   mod->init = NULL;
  587.   mod->unload = NULL;
  588.  
  589.   /* Count of times main gimp is within the module.  Normally, this
  590.    * will be 1, and we assume that the module won't call its
  591.    * unload callback until it is satisfied that it's not in use any
  592.    * more.  refs can be 2 temporarily while we're running the module's
  593.    * unload function, to stop the module attempting to unload
  594.    * itself. */
  595.   mod->refs = 0;
  596.  
  597.   mod->load_inhibit = module_inhibited (mod->fullpath, module_db_load_inhibit);
  598.   if (!mod->load_inhibit)
  599.     {
  600.       if (be_verbose)
  601.     g_print (_("load module: \"%s\"\n"), filename);
  602.  
  603.       mod_load (mod, TRUE);
  604.     }
  605.   else
  606.     {
  607.       if (be_verbose)
  608.     g_print (_("skipping module: \"%s\"\n"), filename);
  609.  
  610.       mod->state = ST_UNLOADED_OK;
  611.     }
  612.  
  613.   gimp_set_add (modules, mod);
  614. }
  615.  
  616. static void
  617. mod_load (ModuleInfo *mod, 
  618.       gboolean    verbose)
  619. {
  620.   gpointer symbol;
  621.  
  622.   g_return_if_fail (mod->module == NULL);
  623.  
  624.   mod->module = g_module_open (mod->fullpath, G_MODULE_BIND_LAZY);
  625.   if (!mod->module)
  626.     {
  627.       mod->state = ST_MODULE_ERROR;
  628.  
  629.       if (mod->last_module_error)
  630.     g_free (mod->last_module_error);
  631.       mod->last_module_error = g_strdup (g_module_error ());
  632.  
  633.       if (verbose)
  634.     g_warning (_("module load error: %s: %s"),
  635.            mod->fullpath, mod->last_module_error);
  636.       return;
  637.     }
  638.  
  639. #ifdef __EMX__
  640.   if (g_module_symbol (mod->module, "gimp_main_funcs", &symbol))
  641.     {
  642.       *(struct main_funcs_struc **)symbol = gimp_main_funcs;
  643.     }
  644. #endif
  645.   /* find the module_init symbol */
  646.   if (!g_module_symbol (mod->module, "module_init", &symbol))
  647.     {
  648.       mod->state = ST_MODULE_ERROR;
  649.  
  650.       if (mod->last_module_error)
  651.     g_free (mod->last_module_error);
  652.       mod->last_module_error = g_strdup ("missing module_init() symbol");
  653.  
  654.       if (verbose)
  655.     g_warning ("%s: module_init() symbol not found", mod->fullpath);
  656.  
  657.       g_module_close (mod->module);
  658.       mod->module = NULL;
  659.       mod->info = NULL;
  660.       return;
  661.     }
  662.  
  663.   /* run module's initialisation */
  664.   mod->init = symbol;
  665.   mod->info = NULL;
  666.   gimp_module_ref (mod); /* loaded modules are assumed to have a ref of 1 */
  667.   if (mod->init (&mod->info) == GIMP_MODULE_UNLOAD)
  668.     {
  669.       mod->state = ST_LOAD_FAILED;
  670.       gimp_module_unref (mod);
  671.       mod->info = NULL;
  672.       return;
  673.     }
  674.  
  675.   /* module is now happy */
  676.   mod->state = ST_LOADED_OK;
  677.   TRC (("loaded module %s, state at %p\n", mod->fullpath, mod));
  678.  
  679.   /* do we have an unload function? */
  680.   if (g_module_symbol (mod->module, "module_unload", &symbol))
  681.     mod->unload = symbol;
  682.   else
  683.     mod->unload = NULL;
  684. }
  685.  
  686.  
  687. static void
  688. mod_unload_completed_callback (void *data)
  689. {
  690.   ModuleInfo *mod = data;
  691.  
  692.   g_return_if_fail (mod->state == ST_UNLOAD_REQUESTED);
  693.  
  694.   /* lose the ref we gave this module when we loaded it,
  695.    * since the module's now happy to be unloaded. */
  696.   gimp_module_unref (mod);
  697.   mod->info = NULL;
  698.  
  699.   mod->state = ST_UNLOADED_OK;
  700.  
  701.   TRC (("module unload completed callback for %p\n", mod));
  702.  
  703.   module_info_modified (mod);
  704. }
  705.  
  706. static void
  707. mod_unload (ModuleInfo *mod, 
  708.         gboolean    verbose)
  709. {
  710.   g_return_if_fail (mod->module != NULL);
  711.   g_return_if_fail (mod->unload != NULL);
  712.  
  713.   if (mod->state == ST_UNLOAD_REQUESTED)
  714.     return;
  715.  
  716.   mod->state = ST_UNLOAD_REQUESTED;
  717.  
  718.   TRC (("module unload requested for %p\n", mod));
  719.  
  720.   /* Send the unload request.  Need to ref the module so we don't
  721.    * accidentally unload it while this call is in progress (eg if the
  722.    * callback is called before the unload function returns). */
  723.   gimp_module_ref (mod);
  724.   mod->unload (mod->info->shutdown_data, mod_unload_completed_callback, mod);
  725.  
  726.   gtk_idle_add ((GtkFunction) mod_idle_unref, (gpointer) mod);
  727. }
  728.  
  729. static gboolean
  730. mod_idle_unref (ModuleInfo *mod)
  731. {
  732.   gimp_module_unref (mod);
  733.  
  734.   return FALSE;
  735. }
  736.  
  737. #ifdef DUMP_DB
  738. static void
  739. print_module_info (gpointer data, 
  740.            gpointer user_data)
  741. {
  742.   ModuleInfo *i = data;
  743.  
  744.   g_print ("\n%s: %s\n",
  745.        i->fullpath, statename[i->state]);
  746.   g_print ("  module:%p  lasterr:%s  init:%p  unload:%p\n",
  747.        i->module, i->last_module_error? i->last_module_error : "NONE",
  748.        i->init, i->unload);
  749.   if (i->info)
  750.     {
  751.       g_print ("  shutdown_data: %p\n"
  752.            "  purpose:   %s\n"
  753.            "  author:    %s\n"
  754.            "  version:   %s\n"
  755.            "  copyright: %s\n"
  756.            "  date:      %s\n",
  757.            i->info->shutdown_data,
  758.            i->info->purpose, i->info->author, i->info->version,
  759.            i->info->copyright, i->info->date);
  760.     }
  761. }
  762. #endif
  763.  
  764.  
  765.  
  766. /**************************************************************/
  767. /* UI functions */
  768.  
  769. static void
  770. browser_popdown_callback (GtkWidget *widget,
  771.               gpointer   data)
  772. {
  773.   gtk_widget_destroy (GTK_WIDGET (data));
  774. }
  775.  
  776. static void
  777. browser_destroy_callback (GtkWidget *widget,
  778.               gpointer   data)
  779. {
  780.   gtk_signal_disconnect_by_data (GTK_OBJECT (modules), data);
  781.   gimp_set_remove_handler (modules, modules_handler);
  782.   g_free (data);
  783. }
  784.  
  785. static void
  786. browser_load_inhibit_callback (GtkWidget *widget,
  787.                    gpointer   data)
  788. {
  789.   BrowserState *st = data;
  790.   gboolean new_value;
  791.  
  792.   g_return_if_fail (st->last_update != NULL);
  793.  
  794.   new_value = ! GTK_TOGGLE_BUTTON (widget)->active;
  795.  
  796.   if (new_value == st->last_update->load_inhibit)
  797.     return;
  798.  
  799.   st->last_update->load_inhibit = new_value;
  800.   module_info_modified (st->last_update);
  801.  
  802.   need_to_rewrite_modulerc = TRUE;
  803. }
  804.  
  805. static void
  806. browser_info_update (ModuleInfo   *mod, 
  807.              BrowserState *st)
  808. {
  809.   gint i;
  810.   const gchar *text[NUM_INFO_LINES - 1];
  811.   gchar *status;
  812.  
  813.   /* only update the info if we're actually showing it */
  814.   if (mod != st->last_update)
  815.     return;
  816.  
  817.   if (!mod)
  818.     {
  819.       for (i=0; i < NUM_INFO_LINES; i++)
  820.     gtk_label_set_text (GTK_LABEL (st->label[i]), "");
  821.       gtk_label_set_text (GTK_LABEL(st->button_label), _("<No modules>"));
  822.       gtk_widget_set_sensitive (GTK_WIDGET (st->button), FALSE);
  823.       gtk_widget_set_sensitive (GTK_WIDGET (st->load_inhibit_check), FALSE);
  824.       return;
  825.     }
  826.  
  827.   if (mod->info)
  828.     {
  829.       text[0] = mod->info->purpose;
  830.       text[1] = mod->info->author;
  831.       text[2] = mod->info->version;
  832.       text[3] = mod->info->copyright;
  833.       text[4] = mod->info->date;
  834.       text[5] = mod->ondisk? _("on disk") : _("only in memory");
  835.     }
  836.   else
  837.     {
  838.       text[0] = "--";
  839.       text[1] = "--";
  840.       text[2] = "--";
  841.       text[3] = "--";
  842.       text[4] = "--";
  843.       text[5] = mod->ondisk? _("on disk") : _("nowhere (click 'refresh')");
  844.     }
  845.  
  846.   if (mod->state == ST_MODULE_ERROR && mod->last_module_error)
  847.     status = g_strdup_printf ("%s (%s)", gettext (statename[mod->state]),
  848.                   mod->last_module_error);
  849.   else
  850.     {
  851.       status = g_strdup (gettext (statename[mod->state]));
  852.     }
  853.  
  854.   for (i=0; i < NUM_INFO_LINES - 1; i++)
  855.     {
  856.       gtk_label_set_text (GTK_LABEL (st->label[i]), gettext (text[i]));
  857.     }
  858.  
  859.   gtk_label_set_text (GTK_LABEL (st->label[NUM_INFO_LINES-1]), status);
  860.  
  861.   g_free (status);
  862.  
  863.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (st->load_inhibit_check),
  864.                 !mod->load_inhibit);
  865.   gtk_widget_set_sensitive (GTK_WIDGET (st->load_inhibit_check), TRUE);
  866.  
  867.   /* work out what the button should do (if anything) */
  868.   switch (mod->state)
  869.     {
  870.     case ST_MODULE_ERROR:
  871.     case ST_LOAD_FAILED:
  872.     case ST_UNLOADED_OK:
  873.       gtk_label_set_text (GTK_LABEL(st->button_label), _("Load"));
  874.       gtk_widget_set_sensitive (GTK_WIDGET (st->button), mod->ondisk);
  875.       break;
  876.  
  877.     case ST_UNLOAD_REQUESTED:
  878.       gtk_label_set_text (GTK_LABEL(st->button_label), _("Unload"));
  879.       gtk_widget_set_sensitive (GTK_WIDGET (st->button), FALSE);
  880.       break;
  881.  
  882.     case ST_LOADED_OK:
  883.       gtk_label_set_text (GTK_LABEL(st->button_label), _("Unload"));
  884.       gtk_widget_set_sensitive (GTK_WIDGET (st->button),
  885.                 mod->unload? TRUE : FALSE);
  886.       break;    
  887.     }
  888. }
  889.  
  890. static void
  891. browser_info_init (BrowserState *st, 
  892.            GtkWidget    *table)
  893. {
  894.   GtkWidget *label;
  895.   gint i;
  896.  
  897.   gchar *text[] =
  898.   {
  899.     N_("Purpose:"),
  900.     N_("Author:"),
  901.     N_("Version:"),
  902.     N_("Copyright:"),
  903.     N_("Date:"),
  904.     N_("Location:"),
  905.     N_("State:")
  906.   };
  907.  
  908.   for (i=0; i < sizeof(text) / sizeof(char *); i++)
  909.     {
  910.       label = gtk_label_new (gettext (text[i]));
  911.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  912.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, i, i+1,
  913.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
  914.       gtk_widget_show (label);
  915.  
  916.       st->label[i] = gtk_label_new ("");
  917.       gtk_misc_set_alignment (GTK_MISC (st->label[i]), 0.0, 0.5);
  918.       gtk_table_attach (GTK_TABLE (st->table), st->label[i], 1, 2, i, i+1,
  919.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
  920.       gtk_widget_show (st->label[i]);
  921.     }
  922.  
  923.   st->load_inhibit_check =
  924.     gtk_check_button_new_with_label (_("Autoload during startup"));
  925.   gtk_widget_show (st->load_inhibit_check);
  926.   gtk_table_attach (GTK_TABLE (table), st->load_inhibit_check,
  927.             0, 2, i, i+1,
  928.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
  929.   gtk_signal_connect (GTK_OBJECT (st->load_inhibit_check), "toggled",
  930.               browser_load_inhibit_callback, st);
  931. }
  932.  
  933. static void
  934. browser_select_callback (GtkWidget *widget, 
  935.              GtkWidget *child)
  936. {
  937.   ModuleInfo *i;
  938.   BrowserState *st;
  939.  
  940.   i = gtk_object_get_user_data (GTK_OBJECT (child));
  941.   st = gtk_object_get_user_data (GTK_OBJECT (widget));
  942.  
  943.   if (st->last_update == i)
  944.     return;
  945.  
  946.   st->last_update = i;
  947.  
  948.   browser_info_update (st->last_update, st);
  949. }
  950.  
  951.  
  952. static void
  953. browser_load_unload_callback (GtkWidget *widget, 
  954.                   gpointer   data)
  955. {
  956.   BrowserState *st = data;
  957.  
  958.   if (st->last_update->state == ST_LOADED_OK)
  959.     mod_unload (st->last_update, FALSE);
  960.   else
  961.     mod_load (st->last_update, FALSE);
  962.  
  963.   module_info_modified (st->last_update);
  964. }
  965.  
  966.  
  967. static void
  968. make_list_item (gpointer data, 
  969.         gpointer user_data)
  970. {
  971.   ModuleInfo   *info = data;
  972.   BrowserState *st = user_data;
  973.   GtkWidget    *list_item;
  974.  
  975.   if (!st->last_update)
  976.     st->last_update = info;
  977.  
  978.   list_item = gtk_list_item_new_with_label (info->fullpath);
  979.  
  980.   gtk_widget_show (list_item);
  981.   gtk_object_set_user_data (GTK_OBJECT (list_item), info);
  982.  
  983.   gtk_container_add (GTK_CONTAINER (st->list), list_item);
  984. }
  985.  
  986.  
  987. static void
  988. browser_info_add (GimpSet      *set, 
  989.           ModuleInfo   *mod, 
  990.           BrowserState *st)
  991. {
  992.   make_list_item (mod, st);
  993. }
  994.  
  995.  
  996. static void
  997. browser_info_remove (GimpSet      *set, 
  998.              ModuleInfo   *mod, 
  999.              BrowserState *st)
  1000. {
  1001.   GList *dlist, *free_list;
  1002.   GtkWidget *list_item;
  1003.   ModuleInfo *i;
  1004.  
  1005.   dlist = gtk_container_children (GTK_CONTAINER (st->list));
  1006.   free_list = dlist;
  1007.  
  1008.   while (dlist)
  1009.   {
  1010.     list_item = dlist->data;
  1011.  
  1012.     i = gtk_object_get_user_data (GTK_OBJECT (list_item));
  1013.     g_return_if_fail (i != NULL);
  1014.  
  1015.     if (i == mod)
  1016.     {
  1017.       gtk_container_remove (GTK_CONTAINER (st->list), list_item);
  1018.       g_list_free(free_list);
  1019.       return;
  1020.     }
  1021.  
  1022.     dlist = dlist->next;
  1023.   }
  1024.  
  1025.   g_warning ("tried to remove module that wasn't in brower's list");
  1026.   g_list_free(free_list);
  1027. }
  1028.  
  1029.  
  1030.  
  1031. static void
  1032. module_db_module_ondisk (gpointer data, 
  1033.              gpointer user_data)
  1034. {
  1035.   ModuleInfo *mod = data;
  1036.   struct stat statbuf;
  1037.   gint ret;
  1038.   gint old_ondisk = mod->ondisk;
  1039.   GSList **kill_list = user_data;
  1040.  
  1041.   ret = stat (mod->fullpath, &statbuf);
  1042.   if (ret != 0)
  1043.     mod->ondisk = FALSE;
  1044.   else
  1045.     mod->ondisk = TRUE;
  1046.  
  1047.   /* if it's not on the disk, and it isn't in memory, mark it to be
  1048.    * removed later. */
  1049.   if (!mod->ondisk && !mod->module)
  1050.     {
  1051.       *kill_list = g_slist_append (*kill_list, mod);
  1052.       mod = NULL;
  1053.     }
  1054.  
  1055.   if (mod && mod->ondisk != old_ondisk)
  1056.     module_info_modified (mod);
  1057. }
  1058.  
  1059.  
  1060. static void
  1061. module_db_module_remove (gpointer data, 
  1062.              gpointer user_data)
  1063. {
  1064.   ModuleInfo *mod = data;
  1065.  
  1066.   gimp_set_remove (modules, mod);
  1067.  
  1068.   module_info_free (mod);
  1069. }
  1070.  
  1071.  
  1072.  
  1073. typedef struct
  1074. {
  1075.   const gchar *search_key;
  1076.   ModuleInfo  *found;
  1077. } find_by_path_closure;
  1078.  
  1079. static void
  1080. module_db_path_cmp (gpointer data, 
  1081.             gpointer user_data)
  1082. {
  1083.   ModuleInfo *mod = data;
  1084.   find_by_path_closure *cl = user_data;
  1085.  
  1086.   if (!strcmp (mod->fullpath, cl->search_key))
  1087.     cl->found = mod;
  1088. }
  1089.  
  1090. static ModuleInfo *
  1091. module_find_by_path (const char *fullpath)
  1092. {
  1093.   find_by_path_closure cl;
  1094.  
  1095.   cl.found = NULL;
  1096.   cl.search_key = fullpath;
  1097.  
  1098.   gimp_set_foreach (modules, module_db_path_cmp, &cl);
  1099.  
  1100.   return cl.found;
  1101. }
  1102.  
  1103.  
  1104.  
  1105. static void
  1106. browser_refresh_callback (GtkWidget *widget, 
  1107.               gpointer   data)
  1108. {
  1109.   GSList *kill_list = NULL;
  1110.  
  1111.   /* remove modules we don't have on disk anymore */
  1112.   gimp_set_foreach (modules, module_db_module_ondisk, &kill_list);
  1113.   g_slist_foreach (kill_list, module_db_module_remove, NULL);
  1114.   g_slist_free (kill_list);
  1115.   kill_list = NULL;
  1116.  
  1117.   /* walk filesystem and add new things we find */
  1118.   datafiles_read_directories (module_path,
  1119.                   module_initialize, 0 /* no flags */);
  1120. }
  1121.  
  1122.  
  1123. static void
  1124. gimp_module_ref (ModuleInfo *mod)
  1125. {
  1126.   g_return_if_fail (mod->refs >= 0);
  1127.   g_return_if_fail (mod->module != NULL);
  1128.   mod->refs++;
  1129. }
  1130.  
  1131. static void
  1132. gimp_module_unref (ModuleInfo *mod)
  1133. {
  1134.   g_return_if_fail (mod->refs > 0);
  1135.   g_return_if_fail (mod->module != NULL);
  1136.  
  1137.   mod->refs--;
  1138.  
  1139.   if (mod->refs == 0)
  1140.     {
  1141.       TRC (("module %p refs hit 0, g_module_closing it\n", mod));
  1142.       g_module_close (mod->module);
  1143.       mod->module = NULL;
  1144.     }
  1145. }
  1146.  
  1147. /* End of module_db.c */
  1148.